#ifndef __platform_tls_h_
#define __platform_ls_h_

#if defined(OS_WINDOWS)
#include <windows.h>
typedef DWORD tlskey_t;
#elif defined(OS_RTTHREAD) && !defined (RT_USING_PTHREADS)
#include "rtthread.h"
typedef int tlskey_t;
#elif defined(OS_FREERTOS)
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
typedef int tlskey_t;
#define freertos_enter_critical() vTaskSuspendAll()
#define freertos_exit_critical() xTaskResumeAll()
#else
#include <pthread.h>
typedef pthread_key_t tlskey_t;
#endif

#if defined(OS_RTOS)
#define PTHREAD_KEY_MAX             32
struct _pthread_key_data
{
    int is_used;
    void (*destructor)(void *parameter);
};
typedef struct _pthread_key_data _pthread_key_data_t;
extern _pthread_key_data_t _thread_keys[PTHREAD_KEY_MAX];
#include <errno.h>
#endif

// Thread Local Storage
//-------------------------------------------------------------------------------------
// int tls_create(tlskey_t* key);
// int tls_destroy(tlskey_t key);
// int tls_setvalue(tlskey_t key, void* value);
// void* tls_getvalue(tlskey_t key);
//-------------------------------------------------------------------------------------

///@return 0-ok, other-error
static inline int tls_create(tlskey_t* key)
{
#if defined(OS_WINDOWS)
	*key = TlsAlloc();
	return TLS_OUT_OF_INDEXES == *key ? GetLastError() : 0;
#elif defined(OS_RTOS)
	uint32_t index;
	#if defined(OS_RTTHREAD) && !defined (RT_USING_PTHREADS)
		rt_enter_critical();
	#elif defined(OS_FREERTOS)
		freertos_enter_critical();
	#endif
	for (index = 0; index < PTHREAD_KEY_MAX; index ++)
	{
		if (_thread_keys[index].is_used == 0)
		{
			_thread_keys[index].is_used = 1;
			_thread_keys[index].destructor = NULL;
			*key = index;

			#if defined(OS_RTTHREAD) && !defined (RT_USING_PTHREADS)
				rt_exit_critical();
			#elif defined(OS_FREERTOS)
				freertos_exit_critical();
			#endif
			return 0;
		}
	}
	#if defined(OS_RTTHREAD) && !defined (RT_USING_PTHREADS)
		rt_exit_critical();
	#elif defined(OS_FREERTOS)
		freertos_exit_critical();
	#endif
	return EAGAIN;
#else
	return pthread_key_create(key, NULL);
#endif
}

///@return 0-ok, other-error
static inline int tls_destroy(tlskey_t key)
{
#if defined(OS_WINDOWS)
	return TlsFree(key) ? 0 : GetLastError();
#elif defined(OS_RTOS)
	if (key >= PTHREAD_KEY_MAX)
		return EINVAL;

	#if defined(OS_RTTHREAD) && !defined (RT_USING_PTHREADS)
		rt_enter_critical();
	#elif defined(OS_FREERTOS)
		freertos_enter_critical();
	#endif
	_thread_keys[key].is_used = 0;
	_thread_keys[key].destructor = 0;
	#if defined(OS_RTTHREAD) && !defined (RT_USING_PTHREADS)
		rt_exit_critical();
	#elif defined(OS_FREERTOS)
		freertos_exit_critical();
	#endif
	return 0;
#else
	return pthread_key_delete(key);
#endif
}

///@return 0-ok, other-error
static inline int tls_setvalue(tlskey_t key, void* value)
{
#if defined(OS_WINDOWS)
	return TlsSetValue(key, value) ? 0 : GetLastError();
#elif defined(OS_RTOS)
	void ** tls;
#if defined(OS_RTTHREAD) && !defined (RT_USING_PTHREADS)
	if (rt_thread_self() == NULL) return EINVAL;

	/* get pthread data from user data of thread */
	tls = (void **)rt_thread_self()->user_data;
#elif defined(OS_FREERTOS)
#endif
	/* check tls area */
	if (tls == NULL)
	{
		tls = (void**)malloc(sizeof(void*) * PTHREAD_KEY_MAX);
	}

	if ((key < PTHREAD_KEY_MAX) && _thread_keys[key].is_used)
	{
		tls[key] = (void *)value;
		return 0;
	}
	return EINVAL;
#else
	return pthread_setspecific(key, value);
#endif
}

static inline void* tls_getvalue(tlskey_t key)
{
#if defined(OS_WINDOWS)
	return TlsGetValue(key);
#elif defined(OS_RTOS)
	void ** tls;
#if defined(OS_RTTHREAD) && !defined (RT_USING_PTHREADS)
	if (rt_thread_self() == NULL) return EINVAL;

	/* get pthread data from user data of thread */
	tls = (void **)rt_thread_self()->user_data;
#elif defined(OS_FREERTOS)

#endif
	if (tls == NULL)
		return NULL;

	if ((key < PTHREAD_KEY_MAX) && (_thread_keys[key].is_used))
		return tls[key];
#else
	return pthread_getspecific(key);
#endif
}

#endif /* !__platform_tls_h_ */
